using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

namespace FShaderInspector
{
    public partial class FShaderGUI : ShaderGUI
    {
        /// <summary> 绘制的标签等级 </summary>
        public float level = 1;

        /// <summary> 是否是展开状态 </summary>
        public bool isOpen = true;

        /// <summary> 当前第一个材质 </summary>
        Material _material;

        /// <summary> 选择的材质组 </summary>
        Material[] _materials;

        /// <summary> 着色器编辑器 </summary>
        MaterialEditor _materialEditor;

        /// <summary> 着色器属性 </summary>
        MaterialProperty[] _properties;

        /// <summary> 首次启用 </summary>
        bool _firstEnable = true;

        /// <summary> 抽屉索引 </summary>
        float _currentDrawerIndex = 1;

        /// <summary> 当前运行索引 </summary>
        float _currentRunIndex;

        /// <summary> 选择面板样式 </summary>
        GUIStyle _layersStyle;

        /// <summary> 基础属性特性数据 </summary>
        readonly List<PropertyAttributeData> _basePropertyAttributes = new();

        /// <summary> 挑选属性特性数据 </summary>
        readonly List<PropertyAttributeData> _selectPropertyAttributes = new();

        /// <summary> 表面类型选项 Opaque Transparent </summary>
        MaterialProperty _surfaceTypeProp;

        /// <summary> 混合选项 Alpha Add 等 </summary>
        MaterialProperty _blendProp;

        /// <summary> 混合模式保持镜面选项 </summary>
        MaterialProperty _blendModePreserveSpecularProp;

        /// <summary> Alpha裁剪选项 </summary>
        MaterialProperty _alphaClipProp;

        /// <summary> 正反面剔除属性 </summary>
        MaterialProperty _cullProp;

        /// <summary> 深度测试属性 </summary>
        MaterialProperty _zTestProp;

        /// <summary> Alpha裁剪阈值属性 </summary>
        MaterialProperty _cutoffProp;

        /// <summary> 接收阴影选项 </summary>
        MaterialProperty _receiveShadowsProp;

        /// <summary> 反射高光选项 </summary>
        MaterialProperty _specularHighlightsProp;

        /// <summary> 环境反射选项 </summary>
        MaterialProperty _environmentReflectionsProp;

        /// <summary> 队列偏移 </summary>
        MaterialProperty _queueOffsetProp;

        public const string FoldoutDrawer = "Foldout";
        public const string SelectDrawer = "Select";
        public const string EnableDrawer = "Enable";
        public const string BreakDrawer = "Break";
        public const string NonzeroDrawer = "Nonzero";
        public const string ShowIfDrawer = "ShowIf";
        public const string HideIfDrawer = "HideIf";

        const string BaseDrawer = "Base";
        const string ScaleOffsetDrawer = "ScaleOffset";
        const string IntEnumDrawer = "IntEnum";
        const string ExistDrawer = "Exist";
        const string TwoToggleDrawer = "TwoToggle";
        const string Vector2Drawer = "Vector2";
        const string Vector3Drawer = "Vector3";
        const string RangeDrawer = "Range";

        public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
        {
            _materialEditor = materialEditor;
            _properties = properties;
            _material = (Material)materialEditor.target;
            _materials = materialEditor.targets.Cast<Material>().ToArray();

            _surfaceTypeProp = TryFindProperty(CommonProperty.SurfaceType);                             // 表面类型：透明度、不透明度
            _blendProp = TryFindProperty(CommonProperty.BlendMode);                                     // 关联不透明度透明度 的混合模式选择
            _blendModePreserveSpecularProp = TryFindProperty(CommonProperty.BlendModePreserveSpecular); // 混合模式保持高光
            _receiveShadowsProp = TryFindProperty(CommonProperty.ReceiveShadows);                       // 接收阴影
            _cullProp = TryFindProperty(CommonProperty.Cull);                                           // 剔除
            _zTestProp = TryFindProperty(CommonProperty.ZTest);                                         // ZTest
            _alphaClipProp = TryFindProperty(CommonProperty.AlphaClip);                                 // Alpha 裁剪
            _cutoffProp = TryFindProperty(CommonProperty.Cutoff);                                       // 关联 Alpha 裁剪阈值
            _specularHighlightsProp = TryFindProperty(CommonProperty.SpecularHighlights);               // 反射高光
            _environmentReflectionsProp = TryFindProperty(CommonProperty.EnvironmentReflections);       // 环境反射
            _queueOffsetProp = TryFindProperty(CommonProperty.QueueOffset);                             // 队列偏移

            SurfaceOptions();
            LabelDrawer();
            AdvancedOptions();

            if (!_firstEnable)
                return;

            _firstEnable = false;
        }

        public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader)
        {
            base.AssignNewShaderToMaterial(material, oldShader, newShader);
            AssetDatabase.SaveAssets();
        }

        /// <summary> 是否是子级 </summary>
        /// <returns>bool</returns>
        public bool IsChild() => _currentRunIndex >= _currentDrawerIndex;

        /// <summary> 设置折页子集 </summary>
        /// <param name="clickLabelName">点击标签名称</param> <param name="leve">标签登记</param> <param name="state">状态</param>
        public void SetFoldedChild(string clickLabelName, int leve, bool state)
        {
            var propNames = new List<string>();

            var startDispose = false;
            for (var i = 0; i < _properties.Length; i++)
            {
                // --- 遍历到设定名称时 开始处理
                if (_properties[i].name == clickLabelName)
                {
                    startDispose = true;
                    continue;
                }

                if (!startDispose)
                    continue;

                // --- 遍历小于层级的加入队列
                var attributes = _material.shader.GetPropertyAttributes(i);
                var currentLeve = 0;
                foreach (var attribute in attributes)
                {
                    var label = attribute.Split('(')[0];
                    var labelProperties = StringUtils.GetLittleBracketsValue(attribute).Split(',');

                    if (label == FoldoutDrawer)
                        currentLeve = int.Parse(labelProperties[0]);
                    else if (label == EnableDrawer)
                        currentLeve = int.Parse(labelProperties[1]);
                    else if (label == SelectDrawer)
                        currentLeve = 1;

                    if (currentLeve > leve)
                        propNames.Add(_properties[i].name);
                    else if (currentLeve == leve)
                        return;
                }

                // --- 遍历处理所有材质球定义
                foreach (var mat in _materials)
                {
                    foreach (var propName in propNames)
                    {
                        var key = EditorCache.instance.GetPropertyKey(mat, propName);
                        EditorCache.instance.SetKey(key,state);
                    }
                }
            }
        }

        /// <summary> 绘制属性 封装保持图片与Lit一致 </summary>
        /// <param name="prop">绘制的属性</param> <param name="scaleOffset">是否有纹理平铺标签</param>
        void DrawProperty(MaterialProperty prop, bool scaleOffset = false)
        {
            var displayName = Localization.FindName(prop.displayName);

            // 保持与LitGUI一致的纹理样式
            if (prop.type == MaterialProperty.PropType.Texture)
            {
                _materialEditor.TexturePropertySingleLine(EditorGUIUtility.TrTextContent(displayName), prop);

                if (scaleOffset)
                {
                    EditorGUI.indentLevel += 2;
                    _materialEditor.TextureScaleOffsetProperty(prop);
                    EditorGUI.indentLevel -= 2;
                }
            }
            else if (prop.type == MaterialProperty.PropType.Vector)
            {
                _materialEditor.Vector4ShaderProperty(prop, displayName);
            }
            else
            {
                _materialEditor.ShaderProperty(prop, displayName);
            }
        }

        /// <summary> 绘制折页GUI </summary>
        /// <param name="rect">Rect</param> <param name="label">标签</param> <param name="color">颜色</param> <param name="line">是否绘制分割线</param>
        public static void DrawFoldoutGUI(Rect rect, GUIContent label, Color color, bool line)
        {
            var guiCol = GUI.color;

            GUI.color = color;
            GUI.Box(new Rect(rect.x, rect.y, rect.width, rect.height + 1), "");

            // 是否绘制线
            if (line)
            {
                GUI.color = Color.black;
                GUI.Box(new Rect(rect.x, rect.y, rect.width, 1), "");
            }

            GUI.color = guiCol;

            GUI.Label(new Rect(rect.x + 32, rect.y, rect.width, rect.height), label.text, EditorStyles.boldLabel);
            label.text = string.Empty;
            GUI.Label(new Rect(rect.x + 15, rect.y, rect.width, rect.height), label, EditorStyles.boldLabel);
        }

        /// <summary> 尝试获取属性 </summary>
        MaterialProperty TryFindProperty(string propName)
        {
            return _material.HasProperty(propName) ? FindProperty(propName, _properties, false) : null;
        }

        /// <summary> 尝试开启关键字 </summary>
        /// <param name="mat">材质球</param> <param name="keyword">关键字</param>
        public static void TryEnableKeyword(Material mat, string keyword)
        {
            if (GetShaderKeywords(mat.shader, out var global, out var local))
            {
                var keywordExists = Array.Exists(global, i => i.Equals(keyword, StringComparison.Ordinal)) ||
                                    Array.Exists(local, i => i.Equals(keyword, StringComparison.Ordinal));
                if (keywordExists)
                    mat.EnableKeyword(keyword);
            }
            else
            {
                mat.EnableKeyword(keyword);
            }
        }

        /// <summary> 尝试关闭关键字 </summary>
        /// <param name="mat">材质球</param> <param name="keyword">关键字</param>
        public static void TryDisableKeyword(Material mat, string keyword)
        {
            if (GetShaderKeywords(mat.shader, out var global, out var local))
            {
                var keywordExists = Array.Exists(global, i => i.Equals(keyword, StringComparison.Ordinal)) ||
                                    Array.Exists(local, i => i.Equals(keyword, StringComparison.Ordinal));
                if (keywordExists)
                    mat.DisableKeyword(keyword);
            }
            else
            {
                mat.DisableKeyword(keyword);
            }
        }

        /// <summary> 过滤属性 </summary>
        /// <param name="prop">属性</param> <returns>bool</returns>
        static bool IsFilterProperty(MaterialProperty prop)
        {
            string[] filterName =
            {
                CommonProperty.SurfaceType,
                CommonProperty.BlendMode,
                CommonProperty.SrcBlend,
                CommonProperty.SrcBlendAlpha,
                CommonProperty.DstBlend,
                CommonProperty.DstBlendAlpha,
                CommonProperty.ZWrite,
                CommonProperty.AlphaToMask,
                CommonProperty.BlendModePreserveSpecular,
                CommonProperty.Cull,
                CommonProperty.ZTest,
                CommonProperty.AlphaClip,
                CommonProperty.Cutoff,
                CommonProperty.QueueOffset,
                CommonProperty.ReceiveShadows
            };

            foreach (var propName in filterName)
            {
                if (prop.name == propName)
                    return true;
            }

            return false;
        }

        /// <summary> 包含标签抽屉 </summary>
        /// <param name="attributes">属性数组</param> <param name="attribute">属性</param> <returns>bool</returns>
        static bool ContainsLabelDrawer(string[] attributes, string attribute)
        {
            foreach (var item in attributes)
            {
                var attributeSp = item.Split('(');
                var label = attributeSp[0];

                if (label == attribute)
                    return true;
            }

            return false;
        }

        /// <summary> 包含标签抽屉 数组 只要包含一个就确定为 ture </summary>
        /// <param name="attributes"> 属性数组 </param><param name="array">包含属性数组</param> <returns>bool</returns>
        static bool ContainsLabelDrawer(string[] attributes, string[] array)
        {
            foreach (var str in array)
            {
                foreach (var attribute in attributes)
                {
                    var attributeSp = attribute.Split('(');
                    var label = attributeSp[0];

                    if (str == label)
                        return true;
                }
            }

            return false;
        }

        /// <summary> 获取临时的 GUILayout Rect </summary>
        /// <returns>Rect</returns>
        public static Rect GetTempRect()
        {
            GUILayout.Label("");
            var btnRect = GUILayoutUtility.GetLastRect();
            btnRect.width += btnRect.x + 2;
            btnRect.x = 0;
            return btnRect;
        }

        /// <summary> 获取 Shader 中所有的宏 </summary>
        /// <param name="shader">目标Shader</param> <param name="global">全局关键字</param> <param name="local">本地关键字</param> <returns>执行是否成功</returns>
        public static bool GetShaderKeywords(Shader shader, out string[] global, out string[] local)
        {
            try
            {
                var globalKeywords = typeof(ShaderUtil).GetMethod("GetShaderGlobalKeywords", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                global = (string[])globalKeywords?.Invoke(null, new object[] { shader });
                var localKeywords = typeof(ShaderUtil).GetMethod("GetShaderLocalKeywords", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                local = (string[])localKeywords?.Invoke(null, new object[] { shader });
                return true;
            }
            catch (Exception e)
            {
                Debug.LogError($"无法获得着色器关键字: {e.Message}");
                global = local = null;
                return false;
            }
        }

        /// <summary> 打印消息 </summary>
        /// <param name="message">消息</param> <param name="obj">object</param>
        public static void LogMessage(string message, Object obj = null)
        {
            Debug.Log($"<color=#FFCC66>FShader Inspector：</color>{message}", obj);
        }

        /// <summary> 打印属性错误消息 </summary>
        /// <param name="prop">属性</param> <param name="label">标签</param> <param name="rightType">正确类型</param> <returns>bool</returns>
        public static bool LogPropTypeError(MaterialProperty prop, string label, MaterialProperty.PropType rightType = MaterialProperty.PropType.Float)
        {
            if (prop.type == rightType) return false;
            var warningMessage = $"<color=#FFCC66>FShader Inspector：</color>'{prop.name}' 的[{label}]标签支持属性类型必须是 '{rightType}'类型。当前使用的是 '{prop.type}'类型， GUI 绘制存在问题！！！";
            Debug.LogWarning(warningMessage);
            return true;
        }
    }
}